import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:build/build.dart';
import 'package:source_gen/source_gen.dart';

import 'route_annotations.dart';

///***********************************************
///文件描述：
///创建人：dgg108698
///创建时间：2020/9/9
///修改人：
///修改时间：
///修改备注：
///***********************************************

class RouteRootGenerator extends GeneratorForAnnotation<AppRouteRoot> {
  @override
  generateForAnnotatedElement(
      Element element, ConstantReader annotation, BuildStep buildStep) {
    return _Write(RouterGenerator.collector).write();
  }
}

class RouterGenerator extends GeneratorForAnnotation<AppRouter> {
  static Collector collector = Collector();

  @override
  generateForAnnotatedElement(
      Element element, ConstantReader annotation, BuildStep buildStep) {
    collector.collect(element, annotation, buildStep);
    return null;
  }
}

class Collector {
  final List<_RouteConfig> routes = List();
  final Set<String> imports = Set();

  ///将注释的页面加入列表
  void collect(
      Element element, ConstantReader annotation, BuildStep buildStep) {
    if (element is! ClassElement) throw _Error('必须是class修饰的类');

    ///注册的路由路径
    String path = annotation.peek('path')?.stringValue;
    if (path == null) throw _Error('path 不能为空');

    ///接收路由参数
    String receiveRoutePathName =
        annotation.peek('receiveRoutePathName')?.stringValue ?? null;

    ///类名称
    String className = element.name;

    ///构造参数列表
    final configList = _resolveParameter(element);
    routes.add(
      _RouteConfig(
        path: path,
        className: className,
        receiveRoutePathName: receiveRoutePathName,
        parameter: configList,
      ),
    );

    ///导包
    if (buildStep.inputId.path.contains('lib/')) {
      importClazz(
        "package:${buildStep.inputId.package}/${buildStep.inputId.path.replaceFirst('lib/', '')}",
      );
    } else {
      importClazz("${buildStep.inputId.path}");
    }
  }

  List<_ParameterConfig> _resolveParameter(ClassElement element) {
    final constructor = element.unnamedConstructor;
    final params = constructor?.parameters;
    List<_ParameterConfig> configList;

    if (params?.isNotEmpty == true) {
      configList = List();
      for (ParameterElement p in params) {
        configList.add(_ParameterConfig(
          type: p.type,
          name: p.name,
          isPositional: p.isPositional,
          defaultValueCode: p.defaultValueCode,
        ));
      }
    }
    return configList;
  }

  void importClazz(String path) {
    imports.add('import \'$path\'');
  }
}

///注释目标的路由信息
class _RouteConfig {
  ///传入的路由
  final String path;

  ///注释目标的类名称
  final String className;

  final String receiveRoutePathName;

  ///注释目标的构造参数
  final List<_ParameterConfig> parameter;

  _RouteConfig({
    this.path,
    this.className,
    this.receiveRoutePathName,
    this.parameter,
  });
}

///构造参数信息
class _ParameterConfig {
  ///类型名称
  final DartType type;

  ///参数名称
  final String name;

  ///是否可选参数
  final bool isPositional;

  ///默认值代码
  final String defaultValueCode;

  _ParameterConfig({
    this.type,
    this.name,
    this.isPositional,
    this.defaultValueCode,
  });
}

class _Error extends Error {
  final String msg;

  _Error(this.msg);

  @override
  String toString() {
    return msg;
  }
}

class _Write {
  final Collector _collector;

  _Write(this._collector);

  void _paraImport(DartType type) {
    if (type is ParameterizedType) {
      type.typeArguments?.forEach((element) {
        final String imp = element.element.location.components[0];
        if (imp.contains('package')) {
          _collector.imports.add('import \'$imp\'');
        }
        _paraImport(element);
      });
    }
  }

  String write() {
    _collector.imports.add('import \'package:flutter/widgets.dart\'');
    final buffer = StringBuffer();
    _collector.routes.forEach((element) {
      ///在循环内为每个路由增加代码
      buffer.write('\'${element.path}\' : (String routePath,arguments) {\n');
      List constructorParams = [];
      if (element.parameter != null) {
        constructorParams = element.parameter.map((e) {
          String getName;
          if (element.receiveRoutePathName != null &&
              e.name == element.receiveRoutePathName)
            getName = 'routePath';
          else {
            final typeName = e.type.getDisplayString(withNullability: false);
            if (e.type.isDartCoreList) {
              String index = RegExp(r'<(.*?)>').firstMatch(typeName).group(1);
              index = index?.replaceAll(' ', '') ?? '';
              if (index.isEmpty || 'dynamic' != index) {
                //指定了泛型
                getName =
                    'getList${(index?.isEmpty ?? true) ? '' : '<$index>'}(\'${e.name}\',arguments)';
                _paraImport(e.type);
              }
            } else if (e.type.isDartCoreMap) {
              String index = RegExp(r'<(.*?)>').firstMatch(typeName).group(1);
              index = index?.replaceAll(' ', '') ?? '';
              if (index.isEmpty || 'dynamic,dynamic' != index) {
                //指定了泛型
                getName =
                    'getMap${(index?.isEmpty ?? true) ? '' : '<$index>'}(\'${e.name}\',arguments)';
                _paraImport(e.type);
              }
            }
            if (getName == null) {
              getName = 'getValue(\'${e.name}\',arguments)';
            }
          }
          if (e.isPositional) {
            return getName;
          } else {
            return '${e.name}:$getName';
          }
        }).toList();
      }
      buffer.write(
        'return ${element.className}(${constructorParams.join(',')});\n',
      );
      buffer.write('},\n');
    });

    final importBuffer = StringBuffer();
    if (_collector.imports.length > 0) {
      _collector.imports.forEach((element) {
        importBuffer.write(element);
        importBuffer.write(';\n');
      });
    }
    return '''
    
   ${importBuffer.toString()}

  class DggRoutes {
    Map<String, Widget Function(String routePath,dynamic arguments)> getAllPages() {
      return <String, Widget Function(String routePath,dynamic arguments)>{
        ${buffer.toString()}
      };
    }
    getValue(String key, dynamic arguments) {
    if (arguments is Map) {
      return arguments[key];
    }
    return null;
  }
  
  List<T> getList<T>(String key, dynamic arguments) {
    final value = arguments[key];
    if (value is List) {
      return value.cast<T>();
    }
    return null;
  }
  Map<K, V> getMap<K, V>(String key, dynamic arguments) {
    final value = arguments[key];
    if (value is Map) {
      return value.cast<K, V>();
    }
    return null;
  }
  }
    ''';
  }
}
